package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.commen.pojo.param.ChargingProcessParam;
import cn.tedu.charging.commen.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.cost.dao.repository.ChargingDataESRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleCacheRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleRepository;
import cn.tedu.charging.cost.pojo.ChargingData;
import cn.tedu.charging.cost.pojo.po.ChargingCostRulePO;
import cn.tedu.charging.cost.pojo.po.ChargingDataPO;
import cn.tedu.charging.cost.service.CostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class CostServiceImpl implements CostService {
    @Autowired
    private CostRuleRepository costRuleRepository;

    @Autowired
    private ChargingDataESRepository chargingDataESRepository;

    @Autowired
    private CostRuleCacheRepository costRuleCacheRepository;

    private Map<String,ChargingData> chargingDataMap = new HashMap<>();
    @Override
    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
//        ChargingData chargingData = getChargingDataByOrderNo(chargingProcessParam.getOrderNo());
        ChargingDataPO chargingData = getChargingDataPOByOrderNoFromES(chargingProcessParam.getOrderNo(),
                chargingProcessParam.getUserId(),chargingProcessParam.getGunId());
        log.debug("订单号:{},次数:{}",chargingProcessParam.getOrderNo(),chargingData.getCount());

        log.debug("计算充电价格入参:{}",chargingProcessParam);
        Integer hour = getCurrentHour();
        Integer stationId = chargingProcessParam.getStationId();
        Integer gunType = chargingProcessParam.getGunType();
        ChargingCostRulePO matchCostRule = getMatchCostRule(stationId, gunType, hour);
        log.debug("匹配的计费规则:{}",matchCostRule);
        if (matchCostRule != null){
            Float chargingCapacity = chargingProcessParam.getChargingCapacity();
            BigDecimal powerFee = matchCostRule.getPowerFee();
            String name = matchCostRule.getName();
//            if (chargingData.getCount() == 1){
//            if (chargingData.isFirst()){
//                firstCost2ChargingData(chargingCapacity,chargingData,powerFee,hour);
//            }else {
//                lastCost2ChargingData(chargingCapacity,chargingData,powerFee,hour);
//            }
            cost2ChargingData(chargingCapacity,chargingData,powerFee,hour);
        }else {
            log.error("故障，计价规则为空，业务无法继续，需立即处理！");
        }
        ChargingProcessVO chargingProcessVO = buildChargingProcessVO(chargingData);
        log.debug("计算充电价格结果:{}",chargingProcessVO);
        return chargingProcessVO;
    }

    private ChargingProcessVO buildChargingProcessVO(ChargingDataPO chargingData){
        ChargingProcessVO chargingProcessVO = new ChargingProcessVO();
        log.debug("复制前的数据，source:{},target:{}",chargingData,chargingProcessVO);
        BeanUtils.copyProperties(chargingData,chargingProcessVO);
        log.debug("复制后的数据:{}",chargingProcessVO);
        Long startTime = chargingData.getStartTime();
        long now = System.currentTimeMillis();
        Long costTime = now - startTime;
        chargingProcessVO.setTotalTime(costTime);
        return chargingProcessVO;
    }
    private void cost2ChargingData(Float chargingCapacity,ChargingDataPO chargingData,BigDecimal powerFee,Integer hour){
        if (chargingData.isFirst()){
            chargingData.setTotal(getCost(chargingCapacity, powerFee));
        }else {
            Float realChargingCapacity = chargingData.getChargingCapacity(chargingCapacity);
            chargingData.setTotal(getCost(realChargingCapacity,powerFee));
        }
        chargingData.setChargingCapacity(chargingCapacity);
        chargingDataESRepository.save(chargingData);
        log.debug("第{}次同步数据，当前同步时间:{}时,当前总度数:{},当前总花费:{}",chargingData.getCount(),hour,chargingData.getChargingCapacity(),chargingData.getTotal());
    }
    private void firstCost2ChargingData(Float chargingCapacity,ChargingData chargingData,BigDecimal powerFee,Integer hour){
        BigDecimal cost = getCost(chargingCapacity, powerFee);
        chargingData.setChargingCapacity(chargingCapacity);
        chargingData.setTotal(cost);
        log.debug("第一次同步时间:{},当前度数:{},当前花费:{}",hour,chargingCapacity,cost);
    }

    private void lastCost2ChargingData(Float chargingCapacity,ChargingData chargingData,BigDecimal powerFee,Integer hour){
        //获取之前度数
//        Float lastChargingCapacity = chargingData.getChargingCapacity();
        //得到度数差
//        Float realChargingCapacity = chargingCapacity - lastChargingCapacity;
        Float realChargingCapacity = chargingData.getChargingCapacity(chargingCapacity);
        //获取之前总花费
//        BigDecimal lastTotal = chargingData.getTotal();
        //算出本轮花费
        BigDecimal cost = getCost(realChargingCapacity, powerFee);
        //得到当前总花费
//        BigDecimal total = lastTotal.add(cost);
        chargingData.setChargingCapacity(chargingCapacity);
        chargingData.setTotal(cost);
        log.debug("最新同步时间:{},本轮度数:{},本轮花费:{},当前总度数:{},当前总花费:{}",
                hour,realChargingCapacity,cost,chargingCapacity,chargingData.getTotal());
    }

    private ChargingDataPO getChargingDataPOByOrderNoFromES(String orderNo,Integer userId,Integer gunId){
        Optional<ChargingDataPO> optional = chargingDataESRepository.findById(orderNo);
        if (optional.isPresent()){
            ChargingDataPO chargingDataPO = optional.get();
            Integer count = chargingDataPO.getCount();
            count = count + 1;
            chargingDataPO.setCount(count);
            chargingDataESRepository.save(chargingDataPO);
            return chargingDataPO;
        }else {
            ChargingDataPO chargingDataPO = new ChargingDataPO();
            chargingDataPO.setId(orderNo);
            chargingDataPO.setOrderNo(orderNo);
            chargingDataPO.setCount(1);
            chargingDataPO.setUserId(userId);
            chargingDataPO.setStartTime(System.currentTimeMillis());
            chargingDataPO.setGunId(gunId);
            chargingDataESRepository.save(chargingDataPO);
            return chargingDataPO;
        }
    }
    private ChargingData getChargingDataByOrderNo(String orderNo){
        ChargingData chargingData = chargingDataMap.get(orderNo);
        if (chargingData == null){
            chargingData = new ChargingData();
            chargingData.setCount(1);
            chargingDataMap.put(orderNo,chargingData);
        }else {
            Integer count = chargingData.getCount();
            chargingData.setCount(count + 1);
        }
        return chargingData;
    }
    private BigDecimal getCost(Float chargingCapacity,BigDecimal powerFee){
        BigDecimal capacity = new BigDecimal(chargingCapacity);
        BigDecimal cost = capacity.multiply(powerFee);
        return cost;
    }

    private Integer getCurrentHour(){
        return LocalDateTime.now().getHour();
    }

    private ChargingCostRulePO getMatchCostRule(Integer stationId,Integer gunType,Integer hour){
        long start = System.currentTimeMillis();
        log.debug("根据充电站ID和枪头类型获取匹配的计费规则:{},{},{}",stationId,gunType,hour);
        List<ChargingCostRulePO> costRules = getCostRules(stationId);
        long end = System.currentTimeMillis();
        long costTime = end - start;
        log.debug("获取到的计费规则:{},用时:{}",costRules,costTime);
        if (!CollectionUtils.isEmpty(costRules)){
            for (ChargingCostRulePO costRule : costRules) {
                if (costRule.getGunType().equals(gunType)){
                    Integer startTime = costRule.getStartTime();
                    Integer endTime = costRule.getEndTime();
                    if (hour >= startTime && hour < endTime){
                        return costRule;
                    }
                }
            }
        }
        return null;
    }

    private List<ChargingCostRulePO> getCostRules(Integer stationId){
        log.debug("根据充电站ID获取计费规则:{}",stationId);
        List<ChargingCostRulePO> redisCostRules = costRuleCacheRepository.getCostRulesFromRedisByStationId(stationId);
        log.debug("从缓存中获取计费规则:{}",redisCostRules);
        if (!CollectionUtils.isEmpty(redisCostRules)){
            log.debug("从缓存中获取计费规则:{}",redisCostRules);
            return redisCostRules;
        }else {
            log.debug("从数据库中获取计费规则");
            List<ChargingCostRulePO> dbCostRules = costRuleRepository.getCostRulesByStationId(stationId);
            if (!CollectionUtils.isEmpty(dbCostRules)){
                log.debug("从数据库中获取计费规则:{}",dbCostRules);
                costRuleCacheRepository.saveCostRules2Redis(stationId,dbCostRules);
                return dbCostRules;
            }
        }
        return Collections.emptyList();
    }
}
